多线程同步之内核模式


本文主要是对内核模式下的多线程机制进行讲述。与用户模式下的同步机制相比,使用内核对象的同步机制用途更加广泛。



内核对象包括以下几类

  • 进程
  • 线程
  • 作业
  • 事件
  • 可等待的计时器
  • 信号量
  • 互斥量


等待函数

在使用内核对象的同步机制时,就要用到等待函数来判断内核对象是否已经触发,这样就可以确定调用线程是否可被调度。

WaitForSingleObject

1
DWORD WaitForSingleObject(HANDLE hObject, DWORD dwMilliseconds);
  • hObject:表示要等待的内核对象
  • dwMilliseconds:表示线程愿意花费多少时间来等待对象被触发(时间单位:毫秒),INFINITE表示无限等待
  • 函数的返回值:如果线程等待的对象被触发了,则返回WAIT_object_0; 如果因为等待超时,返回WAIT_TIMEOUT; 如果给函数传入的是无限的句柄,则返回WAIT_FAILED;

    WaitForMultipleObject

    与WaitForSingleObject不同之处,WaitForMultipleObject可以等待多个内核对象。

    1
    2
    3
    4
    5
    DWORD WaitForMultipleObject(
    DWORD dwCount,
    CONST HANDLE* phObjects,
    BOOL bWaitAll,
    DWORD dwMilliseconds)
    ;

  • dwCount:等待内核对象的数量

  • phObjects:指针,指向内核对象句柄数组
  • bWaitAll: TRUE,等待所有的内核对象都触发时才返回;FALSE,只要有一个内核对象触发就返回
  • dwMilliseconds:表示线程愿意花费多少时间来等待对象被触发(时间单位:毫秒),INFINITE表示无限等待
  • WaitForMultipleObject的返回值:WAIT_TIMEOUT、WAIT_FAILED与WaitForSingleObject相同;如果设置的是任意一个内核对象触发就返回的话,则返回值是[WAIT_object_0,WAIT_object_0+dwCount-1],得到的数值是内核句柄数组的一个索引;


事件内核对象

事件内核对象包括一个使用计数,一个是否是自动重置事件还是手动重置事件的布尔值,以及另一个表示事件有没有被触发的布尔值。
手动重置事件被触发的时候,正在等待该事件的所有线程都讲变成可调度状态。
自动重置事件被触发的时候,只要一个正在等待该事件的线程会变成可调度状态。

事件内核对象的创建

1
2
3
4
5
HANDLE CreateEvent(
PSECURITY_ATTRIBUTES psa,
BOOL bManualReset,
BOOL bInitialState,
PCTSTR pszName)
;

  • bManualReset:表示是否创建手动重置对象
  • bInitialState:事件初始化为触发状态(TRUE)还是未触发状态(FALSE)
1
2
BOOL SetEvent(HANDLE hEvent);//设置事件触发状态
BOOL ResetEvent(HANDLE hEvent);//设置事件未触发状态


信号量内核对象

信号量内核对象用来对资源进行计数。包含一个使用计数,一个最大资源计数和一个目前资源使用计数。

信号量的规则:

  • 如果当前资源计数大于0,那么信号量处于触发状态
  • 如果当前资源计数等于0,那么信号量处于未触发状态
  • 系统绝对不会让当前资源计数变为负数
  • 当前资源计数绝对不会大于最大资源计数

信号量内核对象的创建

1
2
3
4
5
HANDLE CreateSemaphore(
PSECURITY_ATTRIBUTES psa,
LONG lInitialCount,
LONG lMaximumCount,
PCTSTR pszName)

  • lMaximumCount: 资源的最大使用数量
  • lInitialCount: 初始化时资源中有多少可以使用

每当线程成功等待信号量内核对象,则会将当前使用计数减1,最后线程还要调用ReleaseSemaphore来递增信号量的当前资源使用计数。

1
2
BOOL ReleaseSemaphore(HANDLE hSemaphore,LONG lReleaseCount,PLONG plRreviousCount);
//此函数把lReleaseCount的值加到信号量的当前资源计数上,plRreviousCount返回当前资源计数的原始值


互斥量内核对象

互斥量内核对象用来确保一个一个线程独占对一个资源的访问。互斥量对象包含一个使用计数,线程ID,递归计数。
其中,线程ID用了标识当前占用互斥量的是系统中的哪一个线程,递归计数标识这个线程占用该互斥量的次数。

互斥量对象的规则:

  • 如果线程ID为0,那么该互斥对象不为任何线程所占用,它处于触发状态
  • 如果线程ID非零值,那么有一个线程已经占用了该互斥量,它处于未触发状态

互斥量对象的创建

1
2
3
4
HANDLE CreateMutex(
PSECURITY_ATTRIBUTES psa,
BOOL bInitialOwner,
PCTSTR pszName)

  • bInitialOwner: 互斥量对象的初始状态。FALSE,互斥量对象的线程ID为0,递归计数为0,处于触发状态。
    互斥内核对象的一个特点:系统会检查当前线程的ID与互斥量内部记录的线程ID是否相同。如果ID一致,那么系统会让线程保持可调度状态,即使互斥量尚未触发。
1
BOOL ReleaseMutex(HANDLE hMutex);//释放互斥量,互斥量内部的线程ID置0,递归计数置0,互斥量变成触发状态